home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 726-750 / 729 / ff / source / ff.c next >
C/C++ Source or Header  |  1995-03-18  |  35KB  |  1,337 lines

  1. /***************************************************************************/
  2. /*                   FindFile                   */
  3. /*                 Version 1.01                   */
  4. /*                   August 30, 1992                   */
  5. /*                                       */
  6. /* This is a file-find utility with a 2.0-style GadTools interface.  It    */
  7. /* doesn't support the CLI, although it can be run from the CLI;  it is    */
  8. /* meant as a Workbench utility.                       */
  9. /*                                       */
  10. /* This program is Copyright ©1992 by Dave Schreiber, All Rights Reserved. */
  11. /* This program may not be sold for more than a small copying and shipping */
  12. /* and handling fee, except by written permission of Dave Schreiber.       */
  13. /*                                       */
  14. /* To compile, type:                               */
  15. /*    lmk                                   */
  16. /*                                       */
  17. /* Version List:                               */
  18. /*    1.01 - Changed the default wildcard from '*' to '#?' (since '*'      */
  19. /*         needs to be explicitly turned on under Workbench 2.04 and not */
  20. /*         everyone has it turned on).                   */
  21. /*         August 30, 1992                           */
  22. /*    1.00 - First release (August 27, 1992)                               */
  23. /*                                       */
  24. /***************************************************************************/
  25.  
  26. /*System headers*/
  27.  
  28. #include <exec/types.h>
  29. #include <exec/exec.h>
  30. #include <dos/dos.h>
  31. #include <dos/dosextens.h>
  32. #include <utility/tagitem.h>
  33. #include <intuition/intuition.h>
  34. #include <intuition/gadgetclass.h>
  35.  
  36. #include <libraries/gadtools.h>
  37. #include <libraries/asl.h>
  38. #include <libraries/iffparse.h>
  39. #include <workbench/workbench.h>
  40.  
  41. #include <proto/exec.h>
  42. #include <proto/dos.h>
  43. #include <proto/gadtools.h>
  44. #include <proto/asl.h>
  45. #include <proto/iffparse.h>
  46. #include <proto/intuition.h>
  47. #include <proto/graphics.h>
  48. #include <proto/wb.h>
  49.  
  50. /*Program-specific headers*/
  51.  
  52. #include "FF.h"
  53. #include "GUI.h"
  54. #include "Lists.h"
  55.  
  56. /*SAS/C V5.10 doesn't have these (2.04) function calls in it's copy of*/
  57. /*amiga.lib, so these are necessary*/
  58.  
  59. #pragma libcall DOSBase ParsePatternNoCase 3C6 32103
  60. #pragma libcall DOSBase MatchPatternNoCase 3CC 2102
  61.  
  62.  
  63. /*All the libraries we need*/
  64. struct Library *IntuitionBase,*GfxBase,*GadToolsBase,*AslBase,
  65.            *WorkbenchBase,*IFFParseBase;
  66.  
  67. /*Flags for user settings*/
  68. BOOL Links=FALSE;       /*Recur into linked directories?*/
  69. BOOL Recur=TRUE;       /*Recur at all?*/
  70. BOOL Wildcards=TRUE;       /*Treat the 'Search For:' field as having wildcards?*/
  71.  
  72. /*The linked list of files*/
  73. struct List *fileList=NULL;
  74.  
  75. /*The linked list of a file's path components*/
  76. struct List *dirList=NULL;
  77.  
  78. /*The message port for the AppWindow*/
  79. struct MsgPort *appMsgPort=NULL;
  80.  
  81. /*This holds the AppWindow data*/
  82. struct AppWindow *appWdw=NULL;
  83.  
  84. /*The ordinal number (in the filelist) of the current filename*/
  85. ULONG currentFilename=~0;
  86.  
  87. /*This contains the four pointer images that are animated during a find*/
  88. /*They are not in proper "pointer" format, and must be interleaved and put*/
  89. /*into pointerData, below.  This is done in setupPointerBuffers()*/
  90.  
  91. USHORT PointerImageData[4][26] =
  92. {
  93.    {
  94.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,
  95.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xC000,0x01C0,0x1FC0,
  96.     0xFC00,0xC000,0x01C0,0x1FC0,0xFC00,0xC000,0x01C0,0x1FC0,
  97.     0xFC00,0xC000
  98.    },
  99.    {
  100.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,
  101.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0x01C0,0x1FC0,0xFC00,
  102.     0xC000,0x01C0,0x1FC0,0xFC00,0xC000,0x01C0,0x1FC0,0xFC00,
  103.     0xC000,0x01C0
  104.    },
  105.    {
  106.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,
  107.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0x1FC0,0xFC00,0xC000,
  108.     0x01C0,0x1FC0,0xFC00,0xC000,0x01C0,0x1FC0,0xFC00,0xC000,
  109.     0x01C0,0x1FC0
  110.    },
  111.    {
  112.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,
  113.     0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFFC0,0xFC00,0xC000,0x01C0,
  114.     0x1FC0,0xFC00,0xC000,0x01C0,0x1FC0,0xFC00,0xC000,0x01C0,
  115.     0x1FC0,0xFC00
  116.    }
  117. };
  118.  
  119. USHORT chip pointerData[4][30];
  120.  
  121. /*This keeps track of what 'frame' the pointer 'animation' is at*/
  122. UBYTE pointerCount=0;
  123.  
  124. /*The version string.  Do a 'version FF' to display it*/
  125. char *version="$VER: FindFile V1.01 (30.8.92)";
  126.  
  127. ULONG numFiles;
  128.  
  129. /*****************************************************************************/
  130. /*         The first second contains the kernel of file-finding code         */
  131. /*****************************************************************************/
  132.  
  133. void _main(void)
  134. {
  135.    char Pattern[256];
  136.    char Directory[256];
  137.    FindStat result=FIND_CONTINUE;
  138.  
  139.    /*Copy and interleave the pointer image data*/
  140.    setupPointerBuffers();
  141.  
  142.    /*Open all the libraries that we need*/
  143.    IntuitionBase=(struct Library *)OpenLibrary("intuition.library",37L);
  144.    GfxBase=(struct Library *)OpenLibrary("graphics.library",37L);
  145.    AslBase=(struct Library *)OpenLibrary("asl.library",37L);
  146.    GadToolsBase=(struct Library *)OpenLibrary("gadtools.library",37L);
  147.    WorkbenchBase=(struct Library *)OpenLibrary("workbench.library",37L);
  148.    IFFParseBase=(struct Library *)OpenLibrary("iffparse.library",37L);
  149.  
  150.    /*If any of them didn't open, abort*/
  151.    if(IntuitionBase==NULL || GfxBase==NULL || AslBase==NULL ||
  152.      GadToolsBase==NULL || IFFParseBase==NULL)
  153.       cleanup(100);
  154.  
  155.    /*Get the visual info, etc.*/
  156.    if(SetupScreen()==0)
  157.    {
  158.       /*Open the window*/
  159.       if(OpenProject0Window()==0)
  160.       {
  161.      /*Create the AppWindow message port and, if successful, make*/
  162.      /*the window into an AppWindow*/
  163.      if((appMsgPort=CreateMsgPort())!=NULL)
  164.         appWdw=(struct AppWindow *)AddAppWindowA(1,0,Project0Wnd,
  165.                              appMsgPort,NULL);
  166.  
  167.      /*Loop until the user tells us to quit*/
  168.      /*getUserSelections() either returns FALSE, in which case we*/
  169.      /*quit, or TRUE, in which case we search*/
  170.      while((result!=FIND_CLOSE) && getUserSelections())
  171.      {
  172.         /*Get the 'Search for' string*/
  173.         strcpy(Pattern,((struct StringInfo *)
  174.           (Project0Gadgets[GD_Pattern]->SpecialInfo))->Buffer);
  175.  
  176.         /*If there is no pattern or search string, print an error*/
  177.         if(Pattern[0]==0)
  178.            printError("Please enter a search pattern or name",NULL,NULL);
  179.         else  /*Otherwise, do the search*/
  180.         {
  181.            /*There is no current filename*/
  182.            currentFilename=~0;
  183.  
  184.            /*Create the 'busy' pointer*/
  185.            setBusyPointer();
  186.  
  187.            /*We don't want to be bothered with AppWindow messages while*/
  188.            /*searching*/
  189.            if(appWdw!=NULL)
  190.           RemoveAppWindow(appWdw);
  191.  
  192.            /*Delete an old file list, if one exists, and initialize a*/
  193.            /*fresh list*/
  194.            initFileList();
  195.  
  196.            /*Disable all the gadgets that can be disabled, but enable */
  197.            /*the 'Stop' gadget*/
  198.            disableWindowGadgets();
  199.  
  200.            /*Get the 'Search for' string*/
  201.            strcpy(Pattern,((struct StringInfo *)
  202.              (Project0Gadgets[GD_Pattern]->SpecialInfo))->Buffer);
  203.  
  204.            /*Get the 'Directory' string*/
  205.            strcpy(Directory,((struct StringInfo *)
  206.              (Project0Gadgets[GD_Dir]->SpecialInfo))->Buffer);
  207.  
  208.            numFiles=0;
  209.            /*And do the search*/
  210.            result=findFiles(Directory,Pattern,Recur);
  211.  
  212.            /*The search is finished at this point, so enable all window*/
  213.            /*gadgets except for the Stop gadget, which is disabled*/
  214.            enableWindowGadgets();
  215.  
  216.            /*Restore the old pointer*/
  217.            restorePointer();
  218.  
  219.            /*Reset the AppWindow, if it existed in the first place*/
  220.            if(appMsgPort!=NULL)
  221.           appWdw=(struct AppWindow *)AddAppWindowA(1,0,Project0Wnd,
  222.                                appMsgPort,NULL);
  223.         }
  224.         /*And loop back for another round...*/
  225.      }
  226.  
  227.      /*The user wants to quit, so delete the AppWindow if it was created*/
  228.      if(appWdw!=NULL)
  229.      {
  230.         RemoveAppWindow(appWdw);
  231.         DeleteMsgPort(appMsgPort);
  232.      }
  233.  
  234.      /*Delete the file and directory lists, if any exist*/
  235.      deleteFileList();
  236.      deleteDirList();
  237.  
  238.      /*Close the window*/
  239.      CloseProject0Window();
  240.       }
  241.  
  242.       /*Delete the visual info, etc.*/
  243.       CloseDownScreen();
  244.  
  245.       /*Close libraries and exit with no errors*/
  246.       cleanup(0);
  247.    }
  248. }
  249.  
  250. /*This is the interface between the main part of the program and the*/
  251. /*recursive function that actually finds files.*/
  252. FindStat findFiles(char *startingDir,char *pattern,BOOL recur)
  253. {
  254.    char patResult[516];
  255.    BPTR fileLock;
  256.    UWORD c;
  257.    FindStat result=FIND_CONTINUE;
  258.  
  259.    /*Clear the buffer that will hold the working pattern*/
  260.    for(c=0;c<516;c++)
  261.       patResult[c]=0;
  262.  
  263.    /*If the user specified that the 'Search For' string didn't contain*/
  264.    /*a literal, create a working pattern from the given pattern*/
  265.    if(Wildcards)
  266.       ParsePatternNoCase(pattern,patResult,256);
  267.    else
  268.       /*Otherwise, just use the string as is*/
  269.       strcpy(patResult,pattern);
  270.  
  271.    /*Get a lock on the first directory*/
  272.    fileLock=Lock(startingDir,ACCESS_READ);
  273.  
  274.    if(fileLock==NULL)
  275.       return(FIND_CONTINUE);
  276.  
  277.    /*And start the find*/
  278.    result=doFileFind(fileLock,patResult,recur);
  279.  
  280.    /*We're done, so unlock the lock on the starting directory*/
  281.    UnLock(fileLock);
  282.  
  283.    /*And return the result of the search (quit or continue)*/
  284.    return(result);
  285. }
  286.  
  287. char findFileName[256];          // Needs to be extern
  288.  
  289. /*This is the recursive part of the file-search process.  It goes through*/
  290. /*each file, checking to see if a name matches the given literal/pattern.*/
  291. /*If a directory is found, and if the user specified directory recursion,*/
  292. /*we call doFileFind(), giving it a lock on the subdirectory to search.*/
  293. FindStat doFileFind(BPTR startLock,char *pattern,BOOL recur)
  294. {
  295.    struct TagItem tag;
  296.    struct FileInfoBlock *curFib;
  297.    BPTR oldLock;
  298.    BPTR fileLock;
  299.    BOOL success;
  300.    FindStat result=FIND_CONTINUE;
  301.  
  302.    tag.ti_Tag=TAG_DONE;
  303.    tag.ti_Data=0;
  304.  
  305.    /*Allocate a FileInfoBlock to hold the results from Execute()/ExNext().*/
  306.    if((curFib=AllocDosObject(DOS_FIB,&tag))!=NULL)
  307.    {
  308.       /*Make the search directory into the current directory*/
  309.       oldLock=CurrentDir(startLock);
  310.  
  311.       /*Get the name of the first directory*/
  312.       success=Examine(startLock,curFib);
  313.  
  314.       /*Get the name of the first file/dir in the directory*/
  315.       success=ExNext(startLock,curFib);
  316.  
  317.       /*Loop while we keep finding files and the user doesn't abort*/
  318.       while(success && result==FIND_CONTINUE)
  319.       {
  320.      /*Update the pointer, to let the user know we're still alive*/
  321.      updateBusyPointer();
  322.  
  323.      /*Get the name of the directory that the file is in*/
  324.      NameFromLock(startLock,findFileName,255);
  325.      if(findFileName[0]!=(char)0 &&
  326.                  findFileName[strlen(findFileName)-1]!=':')
  327.         strcat(findFileName,"/");
  328.      strcat(findFileName,curFib->fib_FileName);
  329.  
  330.      /*If we're doing a wildcard search, check to see if the file-*/
  331.      /*name matches the pattern*/
  332.      if(Wildcards)
  333.      {
  334.         if(MatchPatternNoCase(pattern,curFib->fib_FileName))
  335.            updateFileList(findFileName,curFib->fib_FileName,
  336.                           curFib->fib_DirEntryType);
  337.      }
  338.      else
  339.         /*If not, just do a regular string compare*/
  340.         if(stricmp(pattern,curFib->fib_FileName)==0)
  341.            updateFileList(findFileName,curFib->fib_FileName,
  342.                            curFib->fib_DirEntryType);
  343.  
  344.      /*Check to see if the user wants to abort, see a filename*/
  345.      /*listed, etc.*/
  346.      result=checkUserStatus();
  347.  
  348.      /*If this is a directory, and if we can recur into directories,*/
  349.      /*and if this is a link to a directory and recuring into that    */
  350.      /*is OK, and if the user didn't abort, AND if the file isn't   */
  351.      /*a soft link (in which case we can't tell if its a file or a  */
  352.      /*directory), then descend into the directory.*/
  353.      if(curFib->fib_DirEntryType > 0 && recur && result==FIND_CONTINUE
  354.            && (Links || curFib->fib_DirEntryType != ST_LINKDIR)
  355.            && (curFib->fib_DirEntryType != ST_SOFTLINK))
  356.         {
  357.            fileLock=Lock(curFib->fib_FileName,SHARED_LOCK);
  358.            if(fileLock!=NULL)
  359.           result=doFileFind(fileLock,pattern,TRUE);
  360.            UnLock(fileLock);
  361.         }
  362.      else
  363.         numFiles++;
  364.  
  365.      /*Get the next file/dir name*/
  366.      success=ExNext(startLock,curFib);
  367.       }
  368.  
  369.       /*We're done, so restore the current directory to it's entry value*/
  370.       CurrentDir(oldLock);
  371.  
  372.       /*Free the FileInfoBlock that was allocated*/
  373.       FreeDosObject(DOS_FIB,curFib);
  374.    }
  375.  
  376.    /*Return the result of the search*/
  377.    return(result);
  378. }
  379.  
  380. /*This handles closing libraries and exiting.  It will only close those*/
  381. /*libraries that were actually opened.*/
  382. void cleanup(ULONG err)
  383. {
  384.    if(IntuitionBase!=NULL)
  385.       CloseLibrary((struct Library *)IntuitionBase);
  386.  
  387.    if(GfxBase!=NULL)
  388.       CloseLibrary((struct Library *)GfxBase);
  389.  
  390.    if(GadToolsBase!=NULL)
  391.       CloseLibrary(GadToolsBase);
  392.  
  393.    if(AslBase!=NULL)
  394.       CloseLibrary(AslBase);
  395.  
  396.    if(WorkbenchBase!=NULL)
  397.       CloseLibrary(WorkbenchBase);
  398.  
  399.    if(IFFParseBase!=NULL)
  400.       CloseLibrary(IFFParseBase);
  401.  
  402.    exit(err);
  403. }
  404.  
  405. /*****************************************************************************/
  406. /*    The next section is the code that handles computer-human interaction   */
  407. /*****************************************************************************/
  408.  
  409. /*This code handles input before a search takes place*/
  410. /*It returns TRUE if the user wants to start a search, or FALSE if she wants*/
  411. /*to quit*/
  412. BOOL getUserSelections(void)
  413. {
  414.    struct IntuiMessage *mesg;
  415.    ULONG class;
  416.    UWORD code;
  417.    struct Gadget *gad;
  418.    char dirName[256];
  419.    struct TagItem tags[2];
  420.    ULONG sigbit;
  421.    struct AppMessage *amsg;
  422.  
  423.    tags[0].ti_Tag=GTCB_Checked;
  424.    tags[1].ti_Tag=TAG_DONE;
  425.    tags[1].ti_Data=NULL;
  426.  
  427.    /*Loop until we return*/
  428.    while(TRUE)
  429.    {
  430.       /*Wait for input*/
  431.       if(appWdw!=NULL)
  432.      sigbit=Wait(1<<Project0Wnd->UserPort->mp_SigBit|1<<appMsgPort->mp_SigBit);
  433.       else
  434.      sigbit=Wait(1<<Project0Wnd->UserPort->mp_SigBit);
  435.  
  436.       /*Is it a non-AppWindow event?*/
  437.       if(sigbit & (1<<Project0Wnd->UserPort->mp_SigBit))
  438.       {
  439.      /*Get the message*/
  440.      mesg=GT_GetIMsg(Project0Wnd->UserPort);
  441.  
  442.      /*Loop while messages are forthcoming*/
  443.      while(mesg!=NULL)
  444.      {
  445.         class=mesg->Class;
  446.         code=mesg->Code;
  447.         gad=(struct Gadget *)mesg->IAddress;
  448.         GT_ReplyIMsg(mesg);
  449.  
  450.         /*Determine what class of message was received*/
  451.         switch(class)
  452.         {
  453.            /*Window close*/
  454.            case IDCMP_CLOSEWINDOW:
  455.           return(FALSE);
  456.  
  457.            /*Menu choice*/
  458.            case IDCMP_MENUPICK:
  459.           if(!handleMenuInput(code))
  460.              return(FALSE);
  461.           break;
  462.  
  463.            /*Gadget press*/
  464.            case IDCMP_GADGETUP:
  465.           switch(gad->GadgetID)
  466.           {
  467.              /*The three checkbox gadgets*/
  468.              case GD_Recur:
  469.             tags[0].ti_Tag=GA_Disabled;
  470.             if(Recur=!Recur)
  471.                tags[0].ti_Data=FALSE;
  472.             else
  473.                tags[0].ti_Data=TRUE;
  474.  
  475.             GT_SetGadgetAttrsA(Project0Gadgets[GD_Links],
  476.                        Project0Wnd,NULL, tags);
  477.  
  478.             tags[0].ti_Tag=GTCB_Checked;
  479.             break;
  480.              case GD_Links:
  481.             Links=!Links;
  482.             break;
  483.              case GD_Wildcards:
  484.             Wildcards=!Wildcards;
  485.             break;
  486.  
  487.              /*This activates the 'Directory' string gadget when*/
  488.              /*the user presses 'RETURN' while in the 'Search for'*/
  489.              /*gadget.*/
  490.              case GD_Pattern:
  491.             ActivateGadget(Project0Gadgets[GD_Dir],Project0Wnd,NULL);
  492.             break;
  493.  
  494.              /*The browse gadget*/
  495.              case GD_Browse:
  496.             /*Copy the current 'Directory' string into*/
  497.             /*a buffer to give to the file requester*/
  498.             strcpy(dirName,((struct StringInfo *)
  499.                (Project0Gadgets[GD_Dir]->SpecialInfo))->Buffer);
  500.  
  501.             /*Call the file requester (the NULL means that we */
  502.             /*don't want a filename returned, just a directory*/
  503.             browse("Please select a directory",dirName,NULL);
  504.  
  505.             /*Place the directory name into the string gadget*/
  506.             tags[0].ti_Tag=GTST_String;
  507.             tags[0].ti_Data=(ULONG)dirName;
  508.  
  509.             GT_SetGadgetAttrsA(Project0Gadgets[GD_Dir],
  510.                        Project0Wnd,NULL, tags);
  511.  
  512.             /*Restore this value to this array position, so that*/
  513.             /*the VANILLAKEY toggles below don't have to worry*/
  514.             /*about setting it*/
  515.             tags[0].ti_Tag=GTCB_Checked;
  516.             break;
  517.  
  518.              /*The user made a selection from the file list.  Fill*/
  519.              /*the directory component list with appropriate info.*/
  520.              case GD_FileList:
  521.             putInDirList(code);
  522.             break;
  523.  
  524.              /*Start the search*/
  525.              case GD_Go:
  526.             return(TRUE);
  527.             break;
  528.           }
  529.           break;
  530.  
  531.            /*Keyboard gadget equivalents*/
  532.            case IDCMP_VANILLAKEY:
  533.           switch(code)
  534.           {
  535.              /*Browse, same as above*/
  536.              case 'r':
  537.              case 'R':
  538.             strcpy(dirName,((struct StringInfo *)
  539.                (Project0Gadgets[GD_Dir]->SpecialInfo))->Buffer);
  540.  
  541.             browse("Please select a directory",dirName,NULL);
  542.             tags[0].ti_Tag=GTST_String;
  543.             tags[0].ti_Data=(ULONG)dirName;
  544.  
  545.             GT_SetGadgetAttrsA(Project0Gadgets[GD_Dir],
  546.                        Project0Wnd,NULL, tags);
  547.  
  548.             tags[0].ti_Tag=GTCB_Checked;
  549.             break;
  550.  
  551.              /*Search for*/
  552.              case 'f':
  553.              case 'F':
  554.             ActivateGadget(Project0Gadgets[GD_Pattern],Project0Wnd,
  555.                        NULL);
  556.             break;
  557.  
  558.              /*Directory*/
  559.              case 'd':
  560.              case 'D':
  561.             ActivateGadget(Project0Gadgets[GD_Dir],Project0Wnd,NULL);
  562.             break;
  563.  
  564.              /*Go*/
  565.              case 'g':
  566.              case 'G':
  567.             return(TRUE);
  568.  
  569.              /*Subdirectory recursion toggle*/
  570.              case 'b':
  571.              case 'B':
  572.             tags[0].ti_Data=(Recur=!Recur);
  573.             GT_SetGadgetAttrsA(Project0Gadgets[GD_Recur],
  574.                        Project0Wnd,NULL, tags);
  575.             tags[0].ti_Tag=GA_Disabled;
  576.             if(Recur)
  577.                tags[0].ti_Data=FALSE;
  578.             else
  579.                tags[0].ti_Data=TRUE;
  580.  
  581.             GT_SetGadgetAttrsA(Project0Gadgets[GD_Links],
  582.                        Project0Wnd,NULL, tags);
  583.  
  584.             tags[0].ti_Tag=GTCB_Checked;
  585.             break;
  586.  
  587.              /*'Descend into links' toggle*/
  588.              case 'l':
  589.              case 'L':
  590.             if(Recur)
  591.             {
  592.                tags[0].ti_Data=(Links=!Links);
  593.                GT_SetGadgetAttrsA(Project0Gadgets[GD_Links],
  594.                           Project0Wnd,NULL, tags);
  595.             }
  596.             break;
  597.  
  598.              /*'Treat "Search for" as pattern' toggle*/
  599.              case 'w':
  600.              case 'W':
  601.             tags[0].ti_Data=(Wildcards=!Wildcards);
  602.             GT_SetGadgetAttrsA(Project0Gadgets[GD_Wildcards],
  603.                        Project0Wnd,NULL, tags);
  604.             break;
  605.  
  606.           }
  607.           break;
  608.         }
  609.         /*Get the next input event*/
  610.         mesg=GT_GetIMsg(Project0Wnd->UserPort);
  611.      }
  612.       }
  613.  
  614.       /*Check to see if this was an AppWindow event*/
  615.       if(sigbit & (1<<appMsgPort->mp_SigBit))
  616.       {
  617.      BPTR dirLock;
  618.  
  619.      /*Loop while there are AppWindow events, although if the user*/
  620.      /*drags multiple icons into the window at once, only the first*/
  621.      /*one will be used*/
  622.      while((amsg=(struct AppMessage *)GetMsg(appMsgPort))!=NULL)
  623.      {
  624.         /*Get the lock associated with the directory of the icon*/
  625.         dirLock=amsg->am_ArgList[0].wa_Lock;
  626.  
  627.         /*Get the directory name*/
  628.         NameFromLock(dirLock,dirName,255);
  629.  
  630.         /*Place that directory name into the 'Directory' string gadget*/
  631.         tags[0].ti_Tag=GTST_String;
  632.         tags[0].ti_Data=(ULONG)dirName;
  633.  
  634.         GT_SetGadgetAttrsA(Project0Gadgets[GD_Dir],
  635.                    Project0Wnd,NULL, tags);
  636.         tags[0].ti_Tag=GTCB_Checked;
  637.  
  638.         /*Reply to the AppWindow message*/
  639.         ReplyMsg((struct Message *)amsg);
  640.      }
  641.       }
  642.    }
  643. }
  644.  
  645. /*Prepare window gadgets for the start of a search*/
  646. void disableWindowGadgets(void)
  647. {
  648.    struct TagItem tags[2];
  649.    UBYTE gad;
  650.  
  651.    tags[0].ti_Tag=GA_Disabled;
  652.    tags[0].ti_Data=TRUE;
  653.    tags[1].ti_Tag=TAG_DONE;
  654.    tags[1].ti_Data=0;
  655.  
  656.    /*Disable each gadget, except for 'Stop' (which is already disabled)*/
  657.    for(gad=0;gad<10;gad++)
  658.    {
  659.       /*and the two listview gadgets (the file and dir lists), since*/
  660.       /*they can't be disabled*/
  661.       if(gad==GD_FileList)
  662.      gad+=2;
  663.  
  664.       GT_SetGadgetAttrsA(Project0Gadgets[gad],Project0Wnd,NULL,tags);
  665.    }
  666.  
  667.    /*Enable the 'Stop' gadget*/
  668.    tags[0].ti_Data=FALSE;
  669.    GT_SetGadgetAttrsA(Project0Gadgets[GD_Stop],Project0Wnd,NULL,tags);
  670.  
  671. }
  672.  
  673. /*The inverse of disableWindowGadgets(), called when a search is complete*/
  674. void enableWindowGadgets(void)
  675. {
  676.    struct TagItem tags[2];
  677.    UBYTE gad;
  678.  
  679.    tags[0].ti_Tag=GA_Disabled;
  680.    tags[0].ti_Data=FALSE;
  681.    tags[1].ti_Tag=TAG_DONE;
  682.    tags[1].ti_Data=0;
  683.  
  684.    /*Enable all gadgets except for the listviews and Stop gadget*/
  685.    for(gad=0;gad<10;gad++)
  686.    {
  687.       if(gad==GD_FileList)
  688.      gad+=2;
  689.  
  690.       if(gad!=GD_Links || Recur)
  691.      GT_SetGadgetAttrsA(Project0Gadgets[gad],Project0Wnd,NULL,tags);
  692.    }
  693.  
  694.    /*Disable the 'Stop' gadget*/
  695.    tags[0].ti_Data=TRUE;
  696.    GT_SetGadgetAttrsA(Project0Gadgets[GD_Stop],Project0Wnd,NULL,tags);
  697. }
  698.  
  699. /*Get user input while a search is progressing*/
  700. FindStat checkUserStatus(void)
  701. {
  702.    struct IntuiMessage *mesg;
  703.    ULONG class;
  704.    UWORD code;
  705.    struct Gadget *gad;
  706.  
  707.    /*Get a message, if one exists (we don't Wait() since we want to return*/
  708.    /*immediately if we don't get input*/
  709.    mesg=GT_GetIMsg(Project0Wnd->UserPort);
  710.    while(mesg!=NULL)
  711.    {
  712.       class=mesg->Class;
  713.       code=mesg->Code;
  714.       gad=(struct Gadget *)mesg->IAddress;
  715.       GT_ReplyIMsg(mesg);
  716.  
  717.       switch(class)
  718.       {
  719.      /*Gadget release;  it can only be one of two*/
  720.      case IDCMP_GADGETUP:
  721.         switch(gad->GadgetID)
  722.         {
  723.            /*Abort the search*/
  724.            case GD_Stop:
  725.           return(FIND_STOP);
  726.  
  727.            /*Put the directory components of the chosen filename into*/
  728.            /*the directory list*/
  729.            case GD_FileList:
  730.           putInDirList(code);
  731.           break;
  732.            break;
  733.         }
  734.  
  735.      /*The keyboard equivalent of 'Stop'*/
  736.      case IDCMP_VANILLAKEY:
  737.         if(code=='s' || code=='S')
  738.            return(FIND_STOP);
  739.         break;
  740.  
  741.      /*Menu selections*/
  742.      /*Needs to be reworked*/
  743.      case IDCMP_MENUPICK:
  744.         if(!handleMenuInput(code))
  745.            return(FIND_CLOSE);
  746.         break;
  747.  
  748.      /*Close gadget*/
  749.      case IDCMP_CLOSEWINDOW:
  750.         return(FIND_CLOSE);
  751.       }
  752.       /*Get the next message*/
  753.       mesg=GT_GetIMsg(Project0Wnd->UserPort);
  754.    }
  755.  
  756.    /*If we've reached this point, the user hasn't pressed 'Stop', so */
  757.    /*continue searching*/
  758.    return(FIND_CONTINUE);
  759. }
  760.  
  761. /*Tags for the file requester*/
  762. struct TagItem fileReqTags[8]=
  763. {
  764.    {ASL_Hail,NULL},
  765.    {ASL_Height,193},
  766.    {ASL_Width,200},
  767.    {ASL_TopEdge,5},
  768.    {ASL_LeftEdge,10},
  769.    {ASL_Dir,NULL},
  770.    {ASL_File,NULL},
  771.    {TAG_DONE,NULL}
  772. };
  773.  
  774. /*This function brings up a file requester*/
  775. /*(hail==The title of the requester window)*/
  776. BOOL browse(char *hail,char *dir,char *name)
  777. {
  778.    BOOL stat;
  779.    struct FileRequester *fr;
  780.    fileReqTags[0].ti_Data=(ULONG)hail;
  781.    fileReqTags[5].ti_Data=(ULONG)dir;
  782.    fileReqTags[6].ti_Data=(ULONG)name;
  783.  
  784.    /*Open the requester*/
  785.    if((fr=(struct FileRequester *)AllocAslRequest(ASL_FileRequest,
  786.                   fileReqTags))!=NULL)
  787.    {
  788.       /*If the user didn't click on CANCEL*/
  789.       if(stat=AslRequest(fr,NULL))
  790.       {
  791.      /*If the user wants the directory name, return it*/
  792.      if(dir!=NULL)
  793.         strcpy(dir,fr->rf_Dir);
  794.  
  795.      /*Likewise for the filename*/
  796.      if(name!=NULL)
  797.         strcpy(name,fr->rf_File);
  798.       }
  799.  
  800.       /*Delete the file requester structure*/
  801.       FreeAslRequest(fr);
  802.    }
  803.  
  804.    return(stat);
  805. }
  806.  
  807. BOOL handleMenuInput(UWORD menuNumber)
  808. {
  809.    while(menuNumber!=MENUNULL)
  810.    {
  811.       if(MENUNUM(menuNumber)==0)
  812.       {
  813.      switch(ITEMNUM(menuNumber))
  814.      {
  815.         case 0:
  816.            if(fileList!=NULL &&
  817.               getItem(fileList,0)!=NULL)
  818.           saveFileList(SUBNUM(menuNumber)==0);
  819.            else
  820.           printError("There is no list to save",NULL,NULL);
  821.            break;
  822.         case 2:
  823.            printError("FindFile V1.01","August 30, 1992",
  824.                    "©1992 Dave Schreiber");
  825.            break;
  826.         case 4:
  827.            return(FALSE);
  828.      }
  829.       }
  830.       else
  831.      if(ITEMNUM(menuNumber)==0)
  832.      {
  833.         if(SUBNUM(menuNumber)==0)
  834.            copyNameToClipboard(TRUE);
  835.         else
  836.            copyNameToClipboard(FALSE);
  837.      }
  838.      else
  839.         if(SUBNUM(menuNumber)==0)
  840.            clipFileList(TRUE);
  841.         else
  842.            clipFileList(FALSE);
  843.  
  844.       menuNumber=((struct MenuItem *)ItemAddress(Project0Menus,menuNumber))->NextSelect;
  845.    }
  846.    return(TRUE);
  847. }
  848.  
  849.  
  850. /*****************************************************************************/
  851. /*         The next section handles the file and directory lists         */
  852. /*****************************************************************************/
  853.  
  854. /*Intialize the file list*/
  855. void initFileList(void)
  856. {
  857.    /*This will delete an old file list, if one exists*/
  858.    deleteFileList();
  859.  
  860.    /*Create a new list*/
  861.    initList(&fileList);
  862.    return;
  863. }
  864.  
  865. /*Add a name to the file list*/
  866. void updateFileList(char *name,char *nameOnly,LONG dirEntryType)
  867. {
  868.    struct TagItem tags[2];
  869.    char prefixedName[40];
  870.  
  871.    tags[0].ti_Tag=GTLV_Labels;
  872.    tags[0].ti_Data=~0L;
  873.    tags[1].ti_Tag=TAG_DONE;
  874.    tags[1].ti_Data=NULL;
  875.  
  876.    switch(dirEntryType)
  877.    {
  878.       case ST_USERDIR:
  879.      strcpy(prefixedName,"(dir)  ");
  880.      break;
  881.       case ST_LINKDIR:
  882.      strcpy(prefixedName,"<hld>  ");
  883.      break;
  884.       case ST_SOFTLINK:
  885.      strcpy(prefixedName,"<sl>   ");
  886.      break;
  887.       case ST_LINKFILE:
  888.      strcpy(prefixedName,"<hl>   ");
  889.      break;
  890.       default:
  891.      strcpy(prefixedName,"       ");
  892.      break;
  893.    }
  894.    strcat(prefixedName,nameOnly);
  895.  
  896.    /*Update the list*/
  897.    Forbid();
  898.    addItem(fileList,name,prefixedName,TRUE);
  899.    Permit();
  900.  
  901.    /*Inform Intuition/Gadtools of the change*/
  902.    tags[0].ti_Data=(ULONG)fileList;
  903.    GT_SetGadgetAttrsA(Project0Gadgets[GD_FileList], Project0Wnd,NULL, tags);
  904.  
  905.    return;
  906. }
  907.  
  908. /*Delete the file list, if one exists*/
  909. void deleteFileList(void)
  910. {
  911.    struct TagItem tags[2];
  912.  
  913.    tags[0].ti_Tag=GTLV_Labels;
  914.    tags[1].ti_Tag=TAG_DONE;
  915.    tags[1].ti_Data=NULL;
  916.  
  917.    /*If there is a list*/
  918.    if(fileList!=NULL)
  919.    {
  920.       tags[0].ti_Data=0L;
  921.  
  922.       /*Detach the list from the file listview gadget*/
  923.       GT_SetGadgetAttrsA(Project0Gadgets[GD_FileList], Project0Wnd,NULL, tags);
  924.  
  925.       /*Delete the list itself*/
  926.       freeList(&fileList);
  927.    }
  928.  
  929.    return;
  930. }
  931.  
  932. /*Put a filename into the directory listview*/
  933. void putInDirList(UWORD code)
  934. {
  935.    dosNameNode *name;
  936.    char filename[256];
  937.    char part[36];
  938.    char finalPart[40];
  939.    char *currentPart;
  940.    int s=0;
  941.    struct TagItem tags[2];
  942.    BOOL first=TRUE;
  943.  
  944.    currentFilename=code;
  945.    tags[0].ti_Tag=GTLV_Labels;
  946.    tags[0].ti_Data=~0L;
  947.    tags[1].ti_Tag=TAG_DONE;
  948.    tags[1].ti_Data=NULL;
  949.  
  950.    /*Delete a previous directory component list, if one exists*/
  951.    deleteDirList();
  952.  
  953.    /*Initialize a new list*/
  954.    initList(&dirList);
  955.  
  956.    /*Get the node corresponding to the name that the user selected*/
  957.    name=getItem(fileList,code);
  958.  
  959.    /*Get the filename*/
  960.    strcpy(filename,name->fullFilename);
  961.  
  962.    /*This pointer points to the part of the filename that we're currently*/
  963.    /*working on*/
  964.    currentPart=filename;
  965.  
  966.    /*Loop until we run out of filename*/
  967.    while(*currentPart!=0)
  968.    {
  969.       s=0;
  970.  
  971.       /*Copy a filename component to 'part'*/
  972.       do
  973.       {
  974.      part[s++]=*currentPart++;
  975.       }
  976.       while(part[s-1]!=':' && part[s-1]!='/' && part[s-1]!=0);
  977.  
  978.       /*If the component ends with a 0, it is the actual filename*/
  979.       if(part[s-1]==0)
  980.       {
  981.      currentPart--;
  982.  
  983.      /*Indent 2 spaces*/
  984.      strcpy(finalPart,"  ");
  985.      strcat(finalPart,&name->node.ln_Name[STARTOFNAME]);
  986.       }
  987.       /*Ends with a ':'?  Its the device name (no indent)*/
  988.       else if(part[s-1]==':')
  989.       {
  990.      part[s]=0;
  991.      strcpy(finalPart,part);
  992.       }
  993.       /*Otherwise, ending with a slash==directory name*/
  994.       else if(part[s-1]=='/')
  995.       {
  996.      part[s-1]=0;
  997.  
  998.      /*Indent with one space*/
  999.      strcpy(finalPart," ");
  1000.      strcat(finalPart,part);
  1001.       }
  1002.  
  1003.  
  1004.       /*If this isn't the first item in the list, detach the list from the*/
  1005.       /*dirlist listview*/
  1006.       if(!first)
  1007.      GT_SetGadgetAttrsA(Project0Gadgets[GD_DirList], Project0Wnd,NULL, tags);
  1008.       else
  1009.      first=FALSE;
  1010.  
  1011.       /*Add the item to the list*/
  1012.       addItem(dirList,finalPart,NULL,FALSE);
  1013.       tags[0].ti_Data=(ULONG)dirList;
  1014.  
  1015.       /*Update the listview*/
  1016.       GT_SetGadgetAttrsA(Project0Gadgets[GD_DirList], Project0Wnd,NULL, tags);
  1017.    }
  1018.  
  1019.    return;
  1020. }
  1021.  
  1022. /*Delete the directory component list, if it exists*/
  1023. void deleteDirList(void)
  1024. {
  1025.    struct TagItem tags[2];
  1026.  
  1027.    tags[0].ti_Tag=GTLV_Labels;
  1028.    tags[0].ti_Data=0L;
  1029.    tags[1].ti_Tag=TAG_DONE;
  1030.    tags[1].ti_Data=NULL;
  1031.  
  1032.    if(dirList!=NULL)
  1033.    {
  1034.       /*Detach the list from the listview*/
  1035.       GT_SetGadgetAttrsA(Project0Gadgets[GD_DirList], Project0Wnd,NULL, tags);
  1036.  
  1037.       /*And delete it*/
  1038.       freeList(&dirList);
  1039.    }
  1040.  
  1041.    return;
  1042. }
  1043.  
  1044. /*****************************************************************************/
  1045. /*          This code handles error and status reporting             */
  1046. /*****************************************************************************/
  1047.  
  1048. struct EasyStruct errorReq=
  1049. {
  1050.    sizeof(struct EasyStruct),
  1051.    0,
  1052.    "Program Request...",
  1053.    NULL,
  1054.    "Ok"
  1055. };
  1056.  
  1057. /*The three possible formatting strings*/
  1058. char *oneLine="%s";
  1059. char *twoLine="%s\n%s";
  1060. char *threeLine="%s\n%s\n%s";
  1061.  
  1062. /*Print a one, two, or three line error message in an EasyRequester*/
  1063. void printError(char *first,char *second,char *third)
  1064. {
  1065.    APTR args[4];
  1066.    args[0]=args[3]=NULL;
  1067.  
  1068.    /*Three lines*/
  1069.    if(third!=NULL)
  1070.    {
  1071.       args[2]=third;
  1072.       args[1]=second;
  1073.       args[0]=first;
  1074.       errorReq.es_TextFormat=threeLine;
  1075.    }
  1076.    /*Two lines*/
  1077.    else if(second!=NULL)
  1078.    {
  1079.       args[1]=second;
  1080.       args[0]=first;
  1081.       errorReq.es_TextFormat=twoLine;
  1082.    }
  1083.    /*One line*/
  1084.    else if(first!=NULL)
  1085.    {
  1086.       args[0]=first;
  1087.       errorReq.es_TextFormat=oneLine;
  1088.    }
  1089.  
  1090.    /*Put up the requester*/
  1091.    EasyRequestArgs(NULL,&errorReq,NULL,args);
  1092.    return;
  1093. }
  1094.  
  1095. /*Copy the pointer imagery from a buffer holding Gadget Image style image data*/
  1096. /*to a chip memory buffer that will hold the images in the interleaved format*/
  1097. /*required by the Amiga's sprite machinery*/
  1098. void setupPointerBuffers(void)
  1099. {
  1100.    UBYTE pointer,value;
  1101.  
  1102.    /*For each of the four pointer animation 'frames'*/
  1103.    for(pointer=0;pointer<4;pointer++)
  1104.    {
  1105.       /*Initialize the required 0 at the beginning and end of the sprite data*/
  1106.       pointerData[pointer][0]=pointerData[pointer][1]=0;
  1107.       pointerData[pointer][28]=pointerData[pointer][29]=0;
  1108.  
  1109.       /*Copy the data over*/
  1110.       for(value=0;value<13;value++)
  1111.       {
  1112.      pointerData[pointer][2*value+2]=PointerImageData[pointer][value];
  1113.      pointerData[pointer][2*value+3]=PointerImageData[pointer][value+13];
  1114.       }
  1115.    }
  1116.    return;
  1117. }
  1118.  
  1119. /*Set the pointer to 'frame' 0 of the busy-pointer animation*/
  1120. void setBusyPointer(void)
  1121. {
  1122.    pointerCount=0;
  1123.  
  1124.    SetPointer(Project0Wnd,pointerData[0],13,10,-1,0);
  1125.    return;
  1126. }
  1127.  
  1128. /*Advance to the next 'frame' of the pointer animation*/
  1129. void updateBusyPointer()
  1130. {
  1131.    /*If at the end, loop back*/
  1132.    if(++pointerCount==4)
  1133.       pointerCount=0;
  1134.  
  1135.    SetPointer(Project0Wnd,pointerData[pointerCount],13,10,-1,0);
  1136.    return;
  1137. }
  1138.  
  1139. /*Remove the busy-pointer animation and restore the system pointer*/
  1140. void restorePointer()
  1141. {
  1142.    ClearPointer(Project0Wnd);
  1143.    return;
  1144. }
  1145.  
  1146. /*****************************************************************************/
  1147. /*           The next section handles clipboard I/O             */
  1148. /*****************************************************************************/
  1149.  
  1150. struct IFFHandle *iff=NULL;
  1151. struct ContextNode *cn;
  1152.  
  1153. /*Open the clipboard and create an iffparse.library parsing instance to*/
  1154. /*be used for communication with the clipboard (all data is written out*/
  1155. /*in FTXT format.*/
  1156. /*Returns TRUE if successful, FALSE if not*/
  1157. BOOL openClipboard(UBYTE unit)
  1158. {
  1159.    /*Get the IFF handle*/
  1160.    if((iff=(struct IFFHandle *)AllocIFF())!=NULL)
  1161.    {
  1162.       /*Open the clipboard*/
  1163.       if((iff->iff_Stream=(ULONG)OpenClipboard(unit))!=NULL)
  1164.       {
  1165.      /*Initialize the IFF handle*/
  1166.      InitIFFasClip(iff);
  1167.  
  1168.      /*and open it*/
  1169.      if(OpenIFF(iff,IFFF_WRITE)==0)
  1170.      {
  1171.         /*Create a 'FORM FTXT' header*/
  1172.         if(PushChunk(iff,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN)==0)
  1173.         {
  1174.            /*Create a 'CHRS' chunk*/
  1175.            if(PushChunk(iff,0,ID_CHRS,IFFSIZE_UNKNOWN)==0)
  1176.           return(TRUE);
  1177.  
  1178.            PopChunk(iff);
  1179.         }
  1180.         CloseIFF(iff);
  1181.      }
  1182.      CloseClipboard((struct ClipboardHandle *)iff->iff_Stream);
  1183.       }
  1184.       FreeIFF(iff);
  1185.    }
  1186.    return(FALSE);
  1187. }
  1188.  
  1189. /*Write a given string to the clipboard*/
  1190. void writeStringToClipboard(char *string,ULONG stringLen)
  1191. {
  1192.    if(stringLen==~0)
  1193.       WriteChunkBytes(iff,string,strlen(string));
  1194.    else
  1195.       WriteChunkBytes(iff,string,stringLen);
  1196.    return;
  1197. }
  1198.  
  1199. /*Close the clipboard*/
  1200. void closeClipboard(void)
  1201. {
  1202.    PopChunk(iff);
  1203.    PopChunk(iff);
  1204.    CloseIFF(iff);
  1205.    CloseClipboard((struct ClipboardHandle *)iff->iff_Stream);
  1206.    FreeIFF(iff);
  1207.  
  1208.    return;
  1209. }
  1210.  
  1211. /*Copy the currently selected filename to the clipboard*/
  1212. void copyNameToClipboard(BOOL full)
  1213. {
  1214.    dosNameNode *name;
  1215.  
  1216.    /*If there is a current filename...*/
  1217.    if(currentFilename!=~0)
  1218.    {
  1219.       /*Get the name*/
  1220.       name=getItem(fileList,currentFilename);
  1221.  
  1222.       /*IF the clipboard was opened successfully*/
  1223.       if(openClipboard(0))
  1224.       {
  1225.      /*Write the full path name*/
  1226.      if(full)
  1227.         writeStringToClipboard(name->fullFilename,~0L);
  1228.      else
  1229.         /*Or just the filename*/
  1230.         writeStringToClipboard(&name->node.ln_Name[STARTOFNAME],~0L);
  1231.  
  1232.      /*Close the clipboard*/
  1233.      closeClipboard();
  1234.       }
  1235.    }
  1236.  
  1237.    return;
  1238. }
  1239.  
  1240. /*Send the entire file list to the clipboard*/
  1241. void clipFileList(BOOL full)
  1242. {
  1243.    dosNameNode *filename=(dosNameNode *)fileList->lh_Head;
  1244.    if(filename==NULL)
  1245.       return;
  1246.  
  1247.    /*Open the clipboard*/
  1248.    if(openClipboard(0))
  1249.    {
  1250.       /*For each name in the list*/
  1251.       while(filename->node.ln_Succ!=NULL)
  1252.       {
  1253.      /*Write either the*/
  1254.      if(full)
  1255.         /*Full directory path*/
  1256.         writeStringToClipboard(filename->fullFilename,~0);
  1257.      else
  1258.         /*Or just the filename*/
  1259.         writeStringToClipboard(&filename->node.ln_Name[STARTOFNAME],~0);
  1260.  
  1261.      /*End each line with a line feed*/
  1262.      writeStringToClipboard("\n",1);
  1263.  
  1264.      /*Get the next filename*/
  1265.      filename=(dosNameNode *)filename->node.ln_Succ;
  1266.       }
  1267.       /*Close the clipboard*/
  1268.       closeClipboard();
  1269.    }
  1270.    return;
  1271. }
  1272.  
  1273.  
  1274. /*****************************************************************************/
  1275. /*        The next section is the code that handles disk I/O         */
  1276. /*****************************************************************************/
  1277.  
  1278. static char saveDirName[256]={NULL};
  1279.  
  1280. static char saveFilename[36]={NULL};
  1281.  
  1282. /*This function save a file list to a file*/
  1283. void saveFileList(BOOL full)
  1284. {
  1285.    char fullFilename[256];
  1286.    dosNameNode *filename;
  1287.    BPTR filePointer;
  1288.    BOOL stat;
  1289.  
  1290.    /*Get the filename*/
  1291.    stat=browse("Please select a filename",saveDirName,saveFilename);
  1292.  
  1293.    /*If we didn't get a name, or the user pressed CANCEL, or there is no*/
  1294.    /*filelist, return*/
  1295.    if(saveFilename[0]==0 || !stat || (fileList==NULL))
  1296.       return;
  1297.  
  1298.    /*Create the full filename*/
  1299.    strcpy(fullFilename,saveDirName);
  1300.    if(fullFilename[strlen(fullFilename)-1]!=':' && fullFilename[0]!=0)
  1301.       strcat(fullFilename,"/");
  1302.    strcat(fullFilename,saveFilename);
  1303.  
  1304.    /*Get the first item in the file list*/
  1305.    filename=getItem(fileList,0);
  1306.  
  1307.    /*Open the file*/
  1308.    if((filePointer=Open(fullFilename,MODE_NEWFILE))!=NULL)
  1309.    {
  1310.       /*Loop through each filename*/
  1311.       while(filename->node.ln_Succ!=NULL)
  1312.       {
  1313.      /*Write out either the */
  1314.      if(full)
  1315.         /*Full pathname*/
  1316.         Write(filePointer,filename->fullFilename,strlen(filename->fullFilename));
  1317.      else
  1318.         /*Or just the filename*/
  1319.         Write(filePointer,&filename->node.ln_Name[STARTOFNAME+1],
  1320.              strlen(filename->node.ln_Name)-STARTOFNAME);
  1321.  
  1322.      /*Append with a carrage return*/
  1323.      Write(filePointer,"\n",1);
  1324.  
  1325.      /*Get the next node*/
  1326.      filename=(dosNameNode *)filename->node.ln_Succ;
  1327.       }
  1328.       /*Close the file*/
  1329.       Close(filePointer);
  1330.    }
  1331.  
  1332.    return;
  1333. }
  1334.  
  1335. /*End of FF.c*/
  1336.  
  1337.